#include "nativeparser.h"

#include <iostream>
#include <stdlib.h> // strtol
#include <errno.h>  // strtol sets errno
#include "calcexception.h"
#include <string.h> // strerror

#include "number.h"

using namespace std;

/** A really constructive constructor.
 * This name could be some static constant, though.
 */
NativeParser::NativeParser() {
  name = "native";
}

/** Parse a number.
 * Parsing is performed effectively by invoking strtol()
 * The error number is checked and, depending on its value, a number is returned
 * or an exception is thrown.
 */
Number *NativeParser::parseNum(string input, size_t &pos) const {
  // strip leading whitespace. Here we avoid using the WHITESPACE constant,
  // because some may think that dash (-) is just a filler when parsing a 
  // number -- but not in the case of the native parser!
  //
  // (that's another question that it was removed from WHITESPACE, after all)
  while(string(" \t").find(input[pos])!=string::npos) pos++;

  // do the good old way
  string candidate = input.substr(pos);
  const char *nptr = candidate.c_str();
  char *endptr = NULL;
  int base = 0;
  errno = 0;
  long val = strtol(nptr, &endptr, base);
  
  // Use cases:
  // a) errno==0 and pointers not equal: parsing ok
  // b) endptr == nptr: nothing found. In this case, 
  //    errno MAY be set to EINVAL but this is not mandatory.
  // c) errno==ERANGE: out of range
  // d) errno== anything else: other error
  
  // Case a)
  if (errno==0 && endptr > nptr) {
    int len = endptr - nptr;
    pos += len;
    Number *n = new Number(val);
    return n;
  // Case b)
  } else if (errno==0) {
    // nothing parsed
    return NOTANUMBER;
  // Case c)
  } else if (errno==ERANGE) {
    throw CalcException("NativeParser: Value out of range");
  // Case d)
  } else {
    char errmsg[1000] = "NativeParser: ";
    strerror_r(errno, errmsg + strlen(errmsg), 999 - strlen(errmsg));
    throw CalcException(errmsg);
  }

  return NOTANUMBER;
}

/** Parse an operator.
 * Actually, an identical assignment (char to char)
 */
char NativeParser::parseOp(string input, size_t &pos) const {
  // strip whitespace. Again, probably not necessary but it won't hurt.
  while(string(" \t").find(input[pos])!=string::npos) pos++;
  char c = input[pos];
  if (string("+-*/").find(c) != string::npos) {
    // valid operator
    pos++;
    return c;
  } else {
    // invalid operator
    return NOTANOPERATOR;
  }
}
